<?php
/*
 * description：
 * author：wh
 * email：
 * createTime：{2019/12/17} {14:30} 
 */

namespace libraries;


use libraries\systemlogs\SqlOperateLog;
use think\Container;
use think\Db;
use think\Paginator;

/**
 * 查询缓存器、监控器
 * 【使用框架链式操作时，配合此类才能发挥完美效果】
 * 举例
 *  function getMemberInIds(string $ids){
        $obj = Db::name($this->table)->whereIn("id",$ids)->where(["is_deleted"=>0]);
        return DbCacheUtility::getAll($obj);
    }
 * Class DbCacheUtility
 * @package libraries
 */
class DbCacheUtility
{
    const NoCacheTime = 0;
    const LowCacheTime = 60 * 1;
    const NormalCacheTime = 60 * 10;
    const HighCacheTime = 60 * 60;
    const LongCacheTime = 60 * 60 * 24;

    /**
     * desc：
     * author：wh
     * @param $object 查询对象
     * @param int $cacheDuration
     * @param bool $is_log
     * @return mixed
     */
    public static function getOne($object, $cacheDuration = self::NoCacheTime, $is_log = true)
    {
        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = $object->fetchSql(true)->find();

        $md5_name = md5($last_sql);

        $key = 'get_one:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = $object->fetchSql(false)->find();
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_one', $is_cache);
        }

        return $result;
    }

    /**
     * desc：
     * author：wh
     * @param $object 查询对象
     * @param int $cacheDuration
     * @param bool $is_log
     * @return mixed
     */
    public static function getAll($object, $cacheDuration = self::NoCacheTime, $is_log = true)
    {

        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = $object->fetchSql(true)->select();

        $md5_name = md5($last_sql);

        $key = 'get_all:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = $object->fetchSql(false)->select();
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'get_all', $is_cache);
        }
        return $result;
    }

    /**
     * desc：
     * author：wh
     * @param $object
     * @param int $page
     * @param int $listRows
     * @param bool $simple
     * @param array $config
     * @param int $cacheDuration
     * @param bool $is_log
     * @return mixed
     */
    static function paginates($object, $page=1, $listRows = null, $simple = false, $config = [], $cacheDuration = self::NoCacheTime, $is_log = true){

        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = $object->limit(abs(1*$page-1) * $listRows, $listRows)->fetchSql(true)->select();

        $md5_name = md5($last_sql);

        $key = 'paginates:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = $object->fetchSql(false)->paginate($listRows, $simple, $config);
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'paginates', $is_cache);
        }
        return $result;
    }

    /**
     * desc：
     * author：wh
     * @param $sql
     * @param array $bind
     * @return mixed
     * @throws \think\db\exception\BindParamException
     * @throws \think\exception\PDOException
     */
    static function queryScalar($sql, $bind = [], $field, $cacheDuration = self::NoCacheTime, $is_log = true)
    {
        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = self::presetBind($sql, $bind);

        $md5_name = md5($last_sql);

        $key = 'query_scalar:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = Db::query($sql, $bind);
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_scalar', $is_cache);
        }

        return $result ? $result[0][$field] : [];
    }

    /**
     * desc：
     * author：wh
     * @param $sql
     * @param array $bind
     * @return mixed
     * @throws \think\db\exception\BindParamException
     * @throws \think\exception\PDOException
     */
    static function queryOne($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
    {
        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = self::presetBind($sql, $bind);

        $md5_name = md5($last_sql);

        $key = 'query_one:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = Db::query($sql, $bind);
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_one', $is_cache);
        }

        return $result ? $result[0] : [];
    }
    /**
     * desc：
     * author：wh
     * @param $sql
     * @param array $bind
     * @return mixed
     * @throws \think\db\exception\BindParamException
     * @throws \think\exception\PDOException
     */
    static function queryAll($sql, $bind = [], $cacheDuration = self::NoCacheTime, $is_log = true)
    {
        $begin_time = Tools::getMillisecond();

        $is_cache = 0;

        $last_sql = self::presetBind($sql, $bind);
//echo $last_sql;die;
        $md5_name = md5($last_sql);

        $key = 'query_all:' . $md5_name;

        if ($cacheDuration == 0) {
            if(cache($key)){
                cache($key, null);
            }
        }

        if (!(cache($key))) {
            $result = Db::query($sql, $bind);
            if ($cacheDuration != 0) {
                cache($key, $result, $cacheDuration);
            }
        } else {
            $is_cache = 1;
            $result = cache($key);
        }

        $end_time = Tools::getMillisecond();

        if($is_log){
            SqlOperateLog::add($last_sql, $end_time, $begin_time, $md5_name, 'query_all', $is_cache);
        }

        return $result;
    }

    /**
     * desc：
     * author：wh
     * @param $sql
     * @param array $bind
     * @return int
     * @throws \think\db\exception\BindParamException
     * @throws \think\exception\PDOException
     */
    static function execute($sql, $bind = []){
        return Db::execute($sql, $bind);
    }

    /**
     * desc：
     * author：wh
     * @param $sql
     * @param array $bind
     * @return mixed
     */
    static function presetBind($sql, $bind = []){
        if($bind){
            foreach ($bind as $key=>$val){
                $sql = str_replace(':'.$key, $val, $sql);
            }
        }
        return $sql;
    }

    static function paginate($sql, $bind, $total, $listRows = null, $simple = false, $config = [])
    {
        //if (is_int($simple)) {
        //    $total  = $simple;
        //    $simple = false;
        //}

        $paginate = Container::get('config')->pull('paginate');

        if (is_array($listRows)) {
            $config   = array_merge($paginate, $listRows);
            $listRows = $config['list_rows'];
        } else {
            $config   = array_merge($paginate, $config);
            $listRows = $listRows ?: $config['list_rows'];
        }

        /** @var Paginator $class */
        $class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
        $page  = isset($config['page']) ? (int) $config['page'] : call_user_func([
            $class,
            'getCurrentPage',
        ], $config['var_page']);

        $page = $page < 1 ? 1 : $page;

        $config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);

        //if (!isset($total) && !$simple) {
        //
        //
        //    unset($this->options['order'], $this->options['limit'], $this->options['page'], $this->options['field']);
        //
        //    $bind    = $this->bind;
        //    $total   = $this->count();
        //    $results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
        //} elseif ($simple) {
        //    $results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
        //    $total   = null;
        //} else {
        //    $results = $this->page($page, $listRows)->select();
        //}

        $results = DbCacheUtility::queryAll($sql, $bind);

        return $class::make($results, $listRows, $page, $total, $simple, $config);
    }
}